﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Golem.Indexer.Data;
using System.ComponentModel;
using System.Net;
using System.IO;
using System.Web;
using Ude;

namespace Golem.Indexer.Indexers
{
    class FtpIndexer : Indexer
    {
        private enum FtpType { DOS, Unix }

        public FtpIndexer()
            : base(Protocol.Ftp)
        {
        }

        private void GetPaths(String path, MachineDescriptor machine)
        {
            List<String> directories;
            List<FileDescriptor> files;

            this.GetDirectories(path, out directories, out files);

            this.IndexFiles(machine, files);

            foreach (var dir in directories)
            {
                if (dir == "." || dir == ".." || dir.StartsWith("-"))   //TODO: Folders named as "-something" can't be opened, this is issue in the library and I don't know how to fix it. From CLI can be used quotes in cd command.
                    continue;

                this.GetPaths(String.Format("{0}/{1}", path, System.Uri.EscapeDataString(dir)), machine);
            }
        }

        private void GetDirectories(string url, out List<String> directories, out List<FileDescriptor> files)    
        {
            directories = new List<string>();
            files = new List<FileDescriptor>();
            Uri uri = new Uri(url, UriKind.Absolute);
            FtpWebRequest FtpRequest = (FtpWebRequest)WebRequest.Create(uri);        
            FtpRequest.KeepAlive = true;
            FtpRequest.ReadWriteTimeout = 10000;        
            FtpRequest.UsePassive = true;        
            FtpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

            FtpWebResponse FtpResponse = null;

            try
            {
                FtpResponse = (FtpWebResponse)FtpRequest.GetResponse();

                using (StreamReader srd = new StreamReader(FtpResponse.GetResponseStream(), Encoding.Default))
                {
                    ICharsetDetector cdet = new CharsetDetector();
                    String detectString = srd.ReadToEnd();
                    byte[] bytes = System.Text.Encoding.Default.GetBytes(detectString);

                    try
                    {
                        cdet.Feed(bytes, 0, bytes.Length);
                        cdet.DataEnd();
                    }
                    catch { }
                    
                    string ResponseBackStr = detectString;

                    if (cdet.Charset == "UTF-8")
                    {
                        System.Text.Encoding encoding = Encoding.GetEncoding(cdet.Charset);
                        ResponseBackStr = encoding.GetString(bytes);
                    }

                    srd.Close();
                    FtpResponse.Close();
                    string[] ListDetails = ResponseBackStr.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);


                    FtpType type = FtpType.Unix;
                    if (ListDetails.Length > 0)
                    {
                        if (ListDetails[0][8] == ' ' && ListDetails[0][9] == ' ')
                            type = FtpType.DOS;
                    }

                    List<string> RtnList = new List<string>();
                    foreach (string listDetail in ListDetails)
                    {
                        var name = listDetail;
                        var index = 0;
                        long size = 0;
                        bool isDir = false;
                        switch (type)
                        {
                            case FtpType.DOS:
                                isDir = listDetail.ToLower().Contains("<dir>");
                                for (int i = 0; i < 3; i++)
                                {
                                    index = name.IndexOf(" ");

                                    if (i == 2 && !isDir)
                                        size = Convert.ToInt64(name.Substring(0, index));

                                    name = name.Substring(index).TrimStart(' ', '\t');
                                }

                                if (isDir)
                                    directories.Add(name);
                                else
                                {
                                    string fullname = String.Format("{0}/{1}", url, name);
                                    var ftype = FileDescriptor.GetTypeOfFile(Path.GetExtension(name));
                                    files.Add(new FileDescriptor(name, fullname, size, ftype));
                                }
                                break;

                            case FtpType.Unix:
                                isDir = listDetail.ToLower().StartsWith("d");

                                for (int i = 0; i < 8; i++)
                                {
                                    index = name.IndexOf(" ");

                                    if (i == 4 && !isDir)
                                        size = Convert.ToInt64(name.Substring(0, index));

                                    name = name.Substring(index).TrimStart(' ', '\t');
                                }

                                if (isDir)
                                    directories.Add(name);
                                else
                                    files.Add(new FileDescriptor(name, String.Format("{0}/{1}", url, name), size, FileDescriptor.GetTypeOfFile(Path.GetExtension(name))));
                                break;
                            default: Console.WriteLine(String.Format("Not supported ftp server on address: {0}", url));
                                break;
                        }
                    }
                }
            }
            catch (WebException e)
            {
                this.ThrowException(new Exception(String.Format("FTP GetDirectories WebException error: {0}\nUrl: {1}", e.Message, url), e));
            }
            catch (Exception ex)
            {
                this.ThrowException(new Exception(String.Format("FTP GetDirectories error: {0}\nUrl: {1}", ex.Message, url), ex));
            }
            finally
            {
                if (FtpResponse != null)
                    FtpResponse.Close();
            }
        }

        protected override void GetFiles(MachineDescriptor machine)
        {
            var address = String.Format("ftp://{0}/%2F", machine.IP);

            try
            {
                this.GetPaths(address, machine);
            }
            catch (Exception ex)
            {
                this.ThrowException(new Exception(String.Format("FTP GetFiles error: {0}", ex.Message), ex));
            }
        }
    }
}
